package strategies;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import base.Environment;
import plots.Plot;
import utils.Debug;

import static base.Environment.Configurations.POO_DISPERSION_FACTOR;


public class PointOfOriginStrategyGenerator extends LocalOptimumStrategyGenerator {
	
	protected static ArrayList<Plot> pointsOfOrigin;
	
	protected Plot pointOfOrigin;
	
	
	public PointOfOriginStrategyGenerator(Environment env) {
		super(env);
		
		if (pointsOfOrigin == null) {
			initPointsOfOrigin(env);
		}
		this.pointOfOrigin = pointsOfOrigin.get((int) (Math.random() * pointsOfOrigin.size()));
	}
	
	
	private static final void initPointsOfOrigin(Environment env) {
		LinkedList<Plot> plots = new LinkedList<Plot>(env.getPlots().getVertices());
		int size = (int) (env.getDouble(POO_DISPERSION_FACTOR) * env.getSize());
		if (size < 1) {
			size = 1;
		} else if (size > env.getSize()) {
			size = env.getSize();
		}
		
		pointsOfOrigin = new ArrayList<Plot>(size);
		for (int i = 0; i < size; i++) {
			pointsOfOrigin.add(plots.remove((int) (Math.random() * plots.size())));
		}
	}

	/**
	 * Generates a new path with a target value of 0 for all nodes.
	 * 
	 * @param length the length of the path
	 * 
	 * @return a new path
	 */
	@Override
	protected LinkedList<PlotTarget> genPath(int length) {
		return genPath(new int[length]);
	}

	@Override
	protected LinkedList<PlotTarget> genPath(int[] targets) {
		ArrayList<Plot> plots = new ArrayList<Plot>(env.getPlots().getVertices());
		plots.remove(pointOfOrigin);
		Collections.shuffle(plots);
		plots.add(0, pointOfOrigin);
		ArrayList<PlotTarget> plotTargets = new ArrayList<PlotTarget>(targets.length);
		for (int i = 0; i < targets.length; i++) {
			plotTargets.add(new PlotTarget(plots.get(i), targets[i]));
		}
		return genPath(plotTargets);
	}

	@Override
	protected LinkedList<PlotTarget> genPath(List<PlotTarget> plotTargets) {
		HashMap<Plot, PlotTarget> plotsToCross = new HashMap<Plot, PlotTarget>();
		for (PlotTarget plotTarget : plotTargets) {
			plotsToCross.put(plotTarget.plot, plotTarget);
		}
		
		LinkedList<PlotTarget> path = new LinkedList<PlotTarget>();
		PlotTarget previous;
		if (plotsToCross.containsKey(pointOfOrigin)) {
			previous = plotsToCross.remove(pointOfOrigin);
		} else {
			previous = new PlotTarget(pointOfOrigin);
		}
		path.add(previous);
		
		while (!plotsToCross.isEmpty()) {
			PlotTarget next = plotsToCross.values().iterator().next();
			List<Plot> intermediate = env.getPath(previous.plot, next.plot);
			for (Plot plot : intermediate) {
				if (plotsToCross.containsKey(plot)) {
					path.add(plotsToCross.remove(plot));
				} else {
					path.add(new PlotTarget(plot));
				}
			}
			path.add(plotsToCross.remove(next.plot));
			previous = next;
		}
		
		if (!previous.plot.equals(pointOfOrigin)) {
			List<Plot> intermediate = env.getPath(previous.plot, pointOfOrigin);
			for (Plot plot : intermediate) {
				path.add(new PlotTarget(plot));
			}
			path.add(new PlotTarget(pointOfOrigin));
		}
		
		return path;
	}
	
	
	/**
	 * Functionality tester for default graph
	 * @param env
	 */
	public static void testFunctionality(Environment env){
		PointOfOriginStrategyGenerator gen = new PointOfOriginStrategyGenerator(env);
		int length = 4;
		int[] targets = {10, 15, 3, 20};
		
		System.out.println("Testing functionality of " + PointOfOriginStrategyGenerator.class.getCanonicalName() + "\n");
		
		System.out.println("--- Random PointOfOrigin Strategy of length " + length + ":");
		PointOfOriginStrategy s1 = new PointOfOriginStrategy(gen.genPath(length));		
		unveilPath(s1);		
		
		System.out.println("--- Random PointOfOrigin Strategy of length " + length +
				" with targets "+ Arrays.toString(targets) +":");
		PointOfOriginStrategy s2 = new PointOfOriginStrategy(gen.genPath(targets));
		unveilPath(s2);
		
		
		// Pause the execution
		Debug.pauseExec("Point of Origin Strategy test end");
	}
}
